{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# scsRPA 的频域与绝热耦合系数积分实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 创建日期：2020-08-24"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这篇文档中，我们会回顾 scsRPA 的实现过程 Zhang, Xu [^Zhang-Xu.JPCL.2019.10]。对于其理论推导，我们基本不作的讨论。\n",
    "\n",
    "我们会大量使用 [前一篇文档](dRPA_Comprehense.ipynb) 的结论与程序。但需要注意，这篇文档中，我们所使用的参考态是 PBE0 而非 PBE，并且分子会选用开壳层分子。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 117,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib notebook\n",
    "\n",
    "from pyscf import gto, dft, scf, cc, mcscf, df\n",
    "import numpy as np\n",
    "import scipy\n",
    "from scipy.linalg import fractional_matrix_power\n",
    "from functools import partial\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "np.set_printoptions(5, linewidth=120, suppress=True)\n",
    "np.einsum = partial(np.einsum, optimize=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 开壳层分子体系与 PBE0 计算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于算法的特殊性，我们需要使用上下自旋不定的开壳层分子来描述具体过程。因此，我们使用小体系 cc-pVTZ 基组下的 OH 自由基分子。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pyscf.gto.mole.Mole at 0x7fb1e5206e20>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mol = gto.Mole()\n",
    "mol.atom = \"\"\"\n",
    "O  0. 0. 0.\n",
    "H  0. 0. 1.\n",
    "\"\"\"\n",
    "mol.basis = \"cc-pVTZ\"\n",
    "mol.verbose = 0\n",
    "mol.spin = 1\n",
    "mol.build()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "RI 基组选用 cc-pVTZ-ri 预定义基组。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pyscf.gto.mole.Mole at 0x7fb2185a00d0>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mol_df = mol.copy()\n",
    "mol_df.basis = \"cc-pVTZ-ri\"\n",
    "mol_df.build()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们首先计算分子的 PBE0 能量，其目的是得到 PBE0 的分子轨道。该计算实例记在 `mf` 中。出于便利，我们使用未经过 DF (Density Fitting) 的自洽场。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-75.68100138443273"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mf = dft.UKS(mol, xc=\"PBE0\").run()\n",
    "mf.e_tot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们随后需要定义与分子或方法有关的变量。大多数情况下，我们仍然使用 $i, j$ 表示占据分子轨道，$a, b$ 表示非占分子轨道，$p, q, r, s$ 表示全部分子轨道。这种表示方法并不是很严格，因为一般情况下开壳层应当使用类似于 $i_\\alpha, j_\\beta$ 等记号；但可以比较好地契合程序编写。\n",
    "\n",
    "$\\mu, \\nu, \\kappa, \\lambda$ 仍然表示原子轨道，$P, Q$ 仍然表示 DF 轨道。$\\sigma, \\gamma$ 在这篇文档中表示 (未指定的) 自旋；$\\alpha, \\beta$ 表示上、下自旋。\n",
    "\n",
    "- `nocc` $(n_\\mathrm{occ}^\\alpha, n_\\mathrm{occ}^\\beta)$ 占据轨道数\n",
    "\n",
    "- `nvir` $(n_\\mathrm{vir}^\\alpha, n_\\mathrm{vir}^\\beta)$ 未占轨道数\n",
    "\n",
    "- `dim_ov` $(n_\\mathrm{occ}^\\alpha n_\\mathrm{vir}^\\alpha, n_\\mathrm{occ}^\\beta n_\\mathrm{vir}^\\beta)$ 占据乘以未占轨道数\n",
    "\n",
    "- `nmo` $n_\\mathrm{MO}$ 分子轨道数，等于占据轨道数 `nao` $n_\\mathrm{AO}$\n",
    "\n",
    "- `naux` $n_\\mathrm{aux}$ Density Fitting 基组轨道数 (或者也称辅助基组 Auxiliary)\n",
    "\n",
    "- `so`, `sv`, `sa` 占据、未占、全轨道分割 (用于程序编写)，变量形式为 `Tuple[slice, slice]`\n",
    "\n",
    "- `eri0_ao` $(\\mu \\nu | \\kappa \\lambda)$ 原子轨道双电子排斥积分"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "nocc, nmo, nao = mol.nelec, mol.nao, mol.nao\n",
    "naux = mol_df.nao\n",
    "nvir = (nmo - nocc[0], nmo - nocc[1])\n",
    "dim_ov = (nocc[0] * nvir[0], nocc[1] * nvir[1])\n",
    "so = slice(0, nocc[0]), slice(0, nocc[1])\n",
    "sv = slice(nocc[0], nmo), slice(nocc[1], nmo)\n",
    "eri0_ao = mol.intor(\"int2e\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `e`, `eo`, `ev` $(e_p^\\alpha, e_p^\\beta)$ 全、占据、未占 PBE 轨道能\n",
    "\n",
    "- `C`, `Co`, `Cv` $(C_{\\mu p}^\\alpha, C_{\\mu p}^\\beta)$ 全、占据、未占 PBE 轨道系数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "e, C = mf.mo_energy, mf.mo_coeff\n",
    "eo, ev = (e[0][so[0]], e[1][so[1]]), (e[0][sv[0]], e[1][sv[1]])\n",
    "Co, Cv = (C[0][:, so[0]], C[1][:, so[1]]), (C[0][:, sv[0]], C[1][:, sv[1]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `eng_xc` PBE0 交换相关能中，仅包含其 GGA 而没有杂化部分的能量 $E_\\mathrm{xc}^\\mathsf{GGA}$\n",
    "\n",
    "- `eng_exactX` 使用 PBE0 轨道所构建的精确交换能 $E_\\mathrm{x}^\\mathsf{exact}$\n",
    "\n",
    "- `eng_HXX` $E^\\mathsf{HXX} = E^\\mathsf{xxRPA}_\\mathrm{tot} - E^\\mathsf{xxRPA}_\\mathrm{c}$ 即除去 RPA 的总能量，它在杂化泛函中写为\n",
    "\n",
    "    $$\n",
    "    E^\\mathsf{HXX} = E^\\mathsf{hGGA} - E_\\mathrm{xc}^\\mathsf{GGA} + (1 - c_\\mathrm{x}) E_\\mathrm{x}^\\mathsf{exact}\n",
    "    $$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-6.671932093325463"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ni = dft.numint.NumInt()\n",
    "eng_xc = ni.nr_uks(mol, mf.grids, \"PBE0\", mf.make_rdm1())[1]\n",
    "eng_xc"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-8.539252705875096"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eng_exactX = - 0.5 * (mf.get_k(mf.make_rdm1()) * mf.make_rdm1()).sum()\n",
    "eng_exactX"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-75.41350882051358"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eng_HXX = mf.e_tot - eng_xc + (1 - ni.hybrid_coeff(mf.xc)) * eng_exactX\n",
    "eng_HXX"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `V_df_ia` $(V_{ia, P}^\\alpha, V_{ia, P}^\\beta)$ DF 3c-2e 积分的导出结果，其具有下述性质：\n",
    "\n",
    "    $$\n",
    "    \\sum_{P} V_{ia,P}^\\sigma V_{jb,P}^\\sigma \\simeq (ia|jb)^\\sigma\n",
    "    $$\n",
    "\n",
    "- `V` 上述张量重塑为矩阵，维度为 $(ia, P)$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "int2c2e = mol_df.intor(\"int2c2e\")\n",
    "int3c2e = df.incore.aux_e2(mol, mol_df)\n",
    "int2c2e_half = scipy.linalg.cholesky(int2c2e, lower=True)\n",
    "V_df_mp2 = scipy.linalg.solve_triangular(int2c2e_half, int3c2e.reshape(-1, naux).T, lower=True).reshape(naux, nao, nao).transpose((1, 2, 0))\n",
    "V_df_ia = (\n",
    "    np.einsum(\"uvP, ui, va -> iaP\", V_df_mp2, Co[0], Cv[0]),\n",
    "    np.einsum(\"uvP, ui, va -> iaP\", V_df_mp2, Co[1], Cv[1]))\n",
    "V = (V_df_ia[0].reshape(dim_ov[0], naux), V_df_ia[1].reshape(dim_ov[1], naux))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `D_ia` $(D_{ia}^\\alpha, D_{ia}^\\beta)$ 轨道能之差：\n",
    "\n",
    "    $$\n",
    "    D_{ia}^\\sigma = \\varepsilon_i^\\sigma - \\varepsilon_a^\\sigma\n",
    "    $$\n",
    "    \n",
    "    其是维度为 $(ia, )$ 的向量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "D_ia = (\n",
    "    - eo[0][:, None] + ev[0][None, :],\n",
    "    - eo[1][:, None] + ev[1][None, :])\n",
    "D = (D_ia[0].flatten(), D_ia[1].flatten())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## dRPA 能量计算回顾"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们在上一篇文档中，提及了 [RI 方法闭壳层 dRPA 相关能计算](dRPA_Comprehense.ipynb#程序实现：$O(N^4)$-的-RI-方法)。开壳层的计算方法也是非常类似的。我们定义函数 `Pi_alpha` 与 `Pi_beta`，它表示两种自旋下的\n",
    "\n",
    "$$\n",
    "\\Pi_{PQ}^\\sigma (\\tilde \\omega) = - \\sum_{ia \\in \\sigma} \\frac{2 V_{ia, P}^\\alpha V_{ia, Q}^\\sigma D_{ia}^\\sigma}{(D_{ia}^\\sigma)^2 + \\tilde \\omega^2}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "或者写为矩阵形式\n",
    "\n",
    "$$\n",
    "\\mathbf{\\Pi}^\\sigma (\\tilde \\omega) = - 2 \\mathbf{V}^{\\sigma \\dagger} \\mathbf{D}^{\\sigma, 1/2} (\\mathbf{D}^\\sigma + \\tilde \\omega^2 \\mathbf{I})^{-1} \\mathbf{D}^{\\sigma, 1/2} \\mathbf{V}^\\sigma\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "Pi_alpha = lambda omega: - 2 * np.einsum(\"dP, d, dQ -> PQ\", V[0], D[0] / (D[0]**2 + omega**2), V[0])\n",
    "Pi_beta  = lambda omega: - 2 * np.einsum(\"dP, d, dQ -> PQ\", V[1], D[1] / (D[1]**2 + omega**2), V[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们额外定义 `Pi_dRPA` 为\n",
    "\n",
    "$$\n",
    "\\Pi_{PQ}^\\mathsf{dRPA} (\\tilde \\omega) = \\sum_{\\sigma \\in \\{ \\alpha, \\beta \\}} \\Pi_{PQ}^\\sigma (\\tilde \\omega) = \\Pi_{PQ}^\\alpha (\\tilde \\omega) + \\Pi_{PQ}^\\beta (\\tilde \\omega)\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "或者写为矩阵形式\n",
    "\n",
    "$$\n",
    "\\mathbf{\\Pi}^\\mathsf{dRPA} (\\tilde \\omega) = \\mathbf{\\Pi}^\\alpha (\\tilde \\omega) + \\mathbf{\\Pi}^\\beta (\\tilde \\omega)\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "Pi_dRPA = lambda omega: Pi_alpha(omega) + Pi_beta(omega)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在频域上格点积分的格点 $\\tilde{\\omega}_g$ 与权重 $w (\\tilde{\\omega}_g)$ 可以通过下述函数获得："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "def gen_leggauss_0_inf(ngrid):\n",
    "    x, w = np.polynomial.legendre.leggauss(ngrid)\n",
    "    return 0.5 * (1 + x) / (1 - x), w / (1 - x)**2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "那么，开壳层的 dRPA 能量 `eng_dRPA` 可以表述为\n",
    "\n",
    "$$\n",
    "\\begin{align}\n",
    "E_\\mathrm{c}^\\mathsf{dRPA}\n",
    "&= \\frac{1}{2 \\pi} \\int_{0}^{+ \\infty} \\big( \\log \\det \\big( \\mathbf{1} - \\mathbf{\\Pi}^\\mathsf{dRPA} (\\tilde \\omega) \\big) + \\mathrm{tr} \\big( \\mathbf{\\Pi}^\\mathsf{dRPA} (\\tilde \\omega) \\big) \\big) \\, \\mathrm{d} \\tilde \\omega \\\\\n",
    "&= \\frac{1}{2 \\pi} \\sum_{g} w(\\tilde{\\omega}_g) \\big( \\log \\det \\big( \\mathbf{1} - \\mathbf{\\Pi}^\\mathsf{dRPA} (\\tilde \\omega_g) \\big) + \\mathrm{tr} \\big( \\mathbf{\\Pi}^\\mathsf{dRPA} (\\tilde \\omega_g) \\big) \\big)\n",
    "\\end{align}\n",
    "$$\n",
    "\n",
    "下面的计算中，对 $\\tilde{\\omega}$ 的积分使用了 40 个格点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.3287828424653143"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eng_dRPA = 0\n",
    "for omega, w_omega in zip(*gen_leggauss_0_inf(40)):\n",
    "    eng_dRPA += 1 / (2 * np.pi) * w_omega * (np.log(np.linalg.det(np.eye(naux) - Pi_RPA(omega))) + Pi_RPA(omega).trace())\n",
    "eng_dRPA"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "因此，dRPA 总能量为"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-75.7422916629789"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eng_HXX + eng_dRPA"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ssRPA 相关能计算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ssRPA (**S**ame-**S**pin **RPA**) 相关能计算式与 dRPA 能量计算式非常相似：\n",
    "\n",
    "$$\n",
    "E_\\mathrm{c}^\\mathsf{ssRPA}\n",
    "= \\frac{1}{2 \\pi} \\sum_{g} \\sum_\\sigma w(\\tilde{\\omega}_g) \\big( \\log \\det \\big( \\mathbf{1} - \\mathbf{\\Pi}^\\sigma (\\tilde \\omega_g) \\big) + \\mathrm{tr} \\big( \\mathbf{\\Pi}^\\sigma (\\tilde \\omega_g) \\big) \\big)\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.19104532917053876"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eng_ssRPA = 0\n",
    "for omega, w_omega in zip(*gen_leggauss_0_inf(40)):\n",
    "    eng_ssRPA += 1 / (2 * np.pi) * w_omega * (np.log(np.linalg.det(np.eye(naux) - Pi_alpha(omega))) + Pi_alpha(omega).trace())\n",
    "    eng_ssRPA += 1 / (2 * np.pi) * w_omega * (np.log(np.linalg.det(np.eye(naux) - Pi_beta(omega))) + Pi_beta(omega).trace())\n",
    "eng_ssRPA"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## osRPA1 相关能计算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "osRPA1 (**RPA**-type **O**pposite-**S**pin coupling Dyson equation terminated at the **First-order**) 所贡献的相关能计算式包含对耦合系数的积分。其较为基础的实现方式需要额外对绝热路径耦合系数 $\\lambda$ 作格点积分 (注意到前一篇文档中使用了 $\\alpha$ 作为耦合系数，但在这篇文档中已经被用作自旋向上记号)。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 耦合系数 $\\lambda$ 积分下的 dRPA 相关能"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们再次回顾 dRPA 的计算。事实上，dRPA 在推导过程中，是先有了关于耦合系数 $\\lambda$ 的积分表达式，后面才有 log det 的表达式。我们现在就将关于 $\\lambda$ 重新写出："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "E_\\mathrm{c}^\\mathsf{dRPA}\n",
    "= \\frac{1}{2 \\pi} \\int_{0}^{+ \\infty} \\mathrm{d} \\tilde \\omega \\, \\mathrm{tr} \\big( \\mathbf{\\Pi}^\\mathsf{dRPA} (\\tilde \\omega) \\big)\n",
    "- \\frac{1}{2 \\pi} \\int_{0}^{+ \\infty} \\mathrm{d} \\tilde \\omega \\int_0^1 \\mathrm{d} \\lambda \\, \\mathrm{tr} \\left( \\frac{\\mathbf{\\Pi}^\\mathsf{dRPA} (\\tilde \\omega)}{\\mathbf{1} - \\lambda \\mathbf{\\Pi}^\\mathsf{dRPA} (\\tilde \\omega)} \\right)\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上式第二项中，尽管矩阵除法的写法并不是很好，但由于我们求的是迹，因此实际写程序的时候我们不需要关心矩阵交换律是否满足。\n",
    "\n",
    "对于 $\\lambda$ 从 0 至 1 的积分可以使用线性缩放后的 Legendre-Gauss 格点实现。下述函数 `gen_leggauss_0_1` 会输出格点 $\\lambda_{g'}$ 与其权重 $w(\\lambda_{g'})$："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [],
   "source": [
    "def gen_leggauss_0_1(ngrid):\n",
    "    x, w = np.polynomial.legendre.leggauss(ngrid)\n",
    "    return 0.5 * (x + 1), 0.5 * w"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "因此，若将上述积分表达式更换为格点求和，则\n",
    "\n",
    "$$\n",
    "E_\\mathrm{c}^\\mathsf{dRPA}\n",
    "= \\frac{1}{2 \\pi} \\sum_g w(\\tilde{\\omega}_g) \\, \\mathrm{tr} \\big( \\mathbf{\\Pi}^\\mathsf{dRPA} (\\tilde{\\omega}_g) \\big)\n",
    "- \\frac{1}{2 \\pi} \\sum_g w(\\tilde{\\omega}_g) \\sum_{g'} w(\\lambda_{g'}) \\, \\mathrm{tr} \\left( \\frac{\\mathbf{\\Pi}^\\mathsf{dRPA} (\\tilde{\\omega}_g)}{\\mathbf{1} - \\lambda_{g'} \\mathbf{\\Pi}^\\mathsf{dRPA} (\\tilde{\\omega}_g)} \\right)\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面的计算中，对 $\\lambda_{g'}$ 的积分使用了 10 个格点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.3287828424662268"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eng_dRPA_ac = 0\n",
    "for omega, w_omega in zip(*gen_leggauss_0_inf(40)):\n",
    "    # Term 1\n",
    "    Pi_dRPA_matrix = Pi_RPA(omega)\n",
    "    eng_dRPA_ac += 1 / (2 * np.pi) * w_omega * Pi_dRPA_matrix.trace()\n",
    "    # Term 2\n",
    "    W_matrix = np.zeros((naux, naux))\n",
    "    for lambd, w_lambd in zip(*gen_leggauss_0_1(10)):\n",
    "        W_matrix += w_lambd * np.linalg.inv(np.eye(naux) - lambd * Pi_dRPA_matrix)\n",
    "    eng_dRPA_ac -= 1 / (2 * np.pi) * w_omega * (W_matrix @ Pi_dRPA_matrix).trace()\n",
    "eng_dRPA_ac"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "留意到上式中使用了中间变量 `W_matrix`:\n",
    "\n",
    "$$\n",
    "\\mathbf{W} (\\tilde \\omega) = \\int_0^1 \\mathrm{d} \\lambda \\big( \\mathbf{1} - \\lambda \\mathbf{\\Pi}^\\mathsf{dRPA} (\\tilde \\omega) \\big)^{-1}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### osRPA1 相关能"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "osRPA1 相关能可以写为"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "E_\\mathrm{c}^\\mathsf{osRPA1}\n",
    "= \\frac{1}{2 \\pi} \\sum_{\\sigma \\in \\{ \\alpha, \\beta \\}} \\int_{0}^{+ \\infty} \\mathrm{d} \\tilde \\omega \\, \\mathrm{tr} \\big( \\mathbf{\\Pi}^\\sigma (\\tilde \\omega) \\big)\n",
    "- \\frac{1}{2 \\pi} \\sum_{\\substack{\\sigma, \\gamma \\in \\{ \\alpha, \\beta \\} \\\\ \\gamma \\neq \\sigma}} \\int_{0}^{+ \\infty} \\mathrm{d} \\tilde \\omega \\int_0^1 \\mathrm{d} \\lambda \\, \\mathrm{tr} \\left( \\frac{\\mathbf{\\Pi}^\\sigma (\\tilde \\omega)}{\\mathbf{1} - \\lambda \\mathbf{\\Pi}^\\gamma (\\tilde \\omega)} \\right)\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "程序编写是类似的。对 $\\tilde{\\omega}_{g}$ 的积分使用 40 个格点，对 $\\lambda_{g'}$ 的积分使用 10 个格点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.17888391093782954"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eng_osRPA1 = 0\n",
    "for omega, w_omega in zip(*gen_leggauss_0_inf(40)):\n",
    "    # Term 1\n",
    "    Pi_alpha_matrix, Pi_beta_matrix = Pi_alpha(omega), Pi_beta(omega)\n",
    "    eng_osRPA1 += 1 / (2 * np.pi) * w_omega * (Pi_alpha_matrix.trace() + Pi_beta_matrix.trace())\n",
    "    # Term 2\n",
    "    W_alpha_matrix, W_beta_matrix = np.zeros((naux, naux)), np.zeros((naux, naux))\n",
    "    for lambd, w_lambd in zip(*gen_leggauss_0_1(10)):\n",
    "        W_alpha_matrix += w_lambd * np.linalg.inv(np.eye(naux) - lambd * Pi_alpha_matrix)\n",
    "        W_beta_matrix  += w_lambd * np.linalg.inv(np.eye(naux) - lambd * Pi_beta_matrix)\n",
    "    eng_osRPA1 += - 1 / (2 * np.pi) * w_omega * ((W_alpha_matrix @ Pi_beta_matrix).trace())\n",
    "    eng_osRPA1 += - 1 / (2 * np.pi) * w_omega * ((W_beta_matrix @ Pi_alpha_matrix).trace())\n",
    "eng_osRPA1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上述过程的总计算复杂度是 $O(n_g n_\\mathrm{aux}^2 n_\\mathrm{occ} n_\\mathrm{vir} + n_g n_{g'} b n_\\mathrm{aux}^3)$，其中第一项是生成 $\\mathbf{\\Pi}^\\sigma (\\tilde \\omega)$ 的复杂度，第二项是格点积分外加矩阵求逆的复杂度，$b$ 代表矩阵求逆复杂度中的 $O(b n^3)$。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 闭壳层下 osRPA1 与 ssRPA 相关能等价的说明"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "留意到对于闭壳层的情况，$\\mathbf{\\Pi}^\\alpha (\\tilde \\omega) = \\mathbf{\\Pi}^\\beta (\\tilde \\omega)$，因此可以使用简化式\n",
    "\n",
    "$$\n",
    "- \\int_0^1 \\mathrm{d} \\lambda \\, \\mathrm{tr} \\left( \\frac{\\mathbf{S}}{\\mathbf{1} - \\lambda \\mathbf{S}} \\right) = \\log \\det \\big( \\mathbf{1} - \\mathbf{S} \\big)\n",
    "$$\n",
    "\n",
    "其中，$\\mathbf{S}$ 是正定的对称矩阵。那么 osRPA1 的表达式很容易地会推到与 ssRPA 完全相同的形式。因此，闭壳层下 $E_\\mathrm{c}^\\mathsf{osRPA1} = E_\\mathrm{c}^\\mathsf{ssRPA}$。但开壳层下两者的数值会有不同。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## scsRPA 能量结算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "随后，我们就可以计算得到各种贡献分项。\n",
    "\n",
    "$$\n",
    "\\begin{align}\n",
    "E_\\mathrm{c}^\\mathsf{osRPA} &= E_\\mathrm{c}^\\mathsf{dRPA} - E_\\mathrm{c}^\\mathsf{ssRPA} \\\\\n",
    "E_\\mathrm{c}^\\mathsf{osRPAr} &= E_\\mathrm{c}^\\mathsf{osRPA} - E_\\mathrm{c}^\\mathsf{osRPA1} \\\\\n",
    "E_\\mathrm{c}^\\mathsf{ssRPA+} &= E_\\mathrm{c}^\\mathsf{ssRPA} + E_\\mathrm{c}^\\mathsf{osRPAr}\n",
    "\\end{align}\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [],
   "source": [
    "eng_osRPA  = eng_dRPA  - eng_ssRPA\n",
    "eng_osRPAr = eng_osRPA - eng_osRPA1\n",
    "eng_ssRPAp = eng_ssRPA + eng_osRPAr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " dRPA      -0.32878284\n",
      "ssRPA      -0.19104533\n",
      "osRPA      -0.13773751\n",
      "osRPA1     -0.17888391\n",
      "osRPAr      0.04114640\n",
      "ssRPA+     -0.14989893\n"
     ]
    }
   ],
   "source": [
    "print(\"{:6}{:16.8f}\".format(\" dRPA\", eng_dRPA))\n",
    "print(\"{:6}{:16.8f}\".format(\"ssRPA\", eng_ssRPA))\n",
    "print(\"{:6}{:16.8f}\".format(\"osRPA\", eng_osRPA))\n",
    "print(\"{:6}{:16.8f}\".format(\"osRPA1\", eng_osRPA1))\n",
    "print(\"{:6}{:16.8f}\".format(\"osRPAr\", eng_osRPAr))\n",
    "print(\"{:6}{:16.8f}\".format(\"ssRPA+\", eng_ssRPAp))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "因此，scsRPA 能量可以写为\n",
    "\n",
    "$$\n",
    "E_\\mathrm{c}^\\mathsf{scsRPA} = \\frac{6}{5} E_\\mathrm{c}^\\mathsf{osRPA1} + \\frac{3}{4} E_\\mathrm{c}^\\mathsf{ssRPA+}\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.327084891771009"
      ]
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eng_scsRPA = 6/5 * eng_osRPA1 + 3/4 * eng_ssRPAp\n",
    "eng_scsRPA"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "事实上，上式也等价于\n",
    "\n",
    "$$\n",
    "E_\\mathrm{c}^\\mathsf{scsRPA} = \\frac{9}{20} E_\\mathrm{c}^\\mathsf{osRPA1} + \\frac{3}{4} E_\\mathrm{c}^\\mathsf{dRPA}\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.327084891771009"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eng_scsRPA = 9/20 * eng_osRPA1 + 3/4 * eng_dRPA\n",
    "eng_scsRPA"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "那么 scsRPA 总能量则表示为"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-75.74059371228459"
      ]
     },
     "execution_count": 98,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eng_HXX + eng_scsRPA"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 氮气解离曲线"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们下面将会复现氮气解离曲线 (Zhang, Xu [^Zhang-Xu.JPCL.2019.10] 文献的 Figure 3)。我们会分别使用到闭壳层与开壳层 scsRPA 的计算结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 270,
   "metadata": {
    "code_folding": []
   },
   "outputs": [],
   "source": [
    "def get_UscsRPA(mol, dfbasis=\"cc-pVTZ-ri\"):\n",
    "    # molecule preparation\n",
    "    mf = dft.UKS(mol, xc=\"PBE0\").run()\n",
    "    eng_PBE0 = mf.e_tot\n",
    "    mol_df = mol.copy()\n",
    "    mol_df.basis = dfbasis\n",
    "    mol_df.build()\n",
    "    # Basic information preparation\n",
    "    nocc, nmo, nao = mol.nelec, mol.nao, mol.nao\n",
    "    naux = mol_df.nao\n",
    "    nvir = (nmo - nocc[0], nmo - nocc[1])\n",
    "    dim_ov = (nocc[0] * nvir[0], nocc[1] * nvir[1])\n",
    "    so = slice(0, nocc[0]), slice(0, nocc[1])\n",
    "    sv = slice(nocc[0], nmo), slice(nocc[1], nmo)\n",
    "    eri0_ao = mol.intor(\"int2e\")\n",
    "    e, C = mf.mo_energy, mf.mo_coeff\n",
    "    eo, ev = (e[0][so[0]], e[1][so[1]]), (e[0][sv[0]], e[1][sv[1]])\n",
    "    Co, Cv = (C[0][:, so[0]], C[1][:, so[1]]), (C[0][:, sv[0]], C[1][:, sv[1]])\n",
    "    # eng_HXX calculation\n",
    "    ni = dft.numint.NumInt()\n",
    "    eng_xc = ni.nr_uks(mol, mf.grids, mf.xc, mf.make_rdm1())[1]\n",
    "    eng_exactX = - 0.5 * (mf.get_k(mf.make_rdm1()) * mf.make_rdm1()).sum()\n",
    "    eng_HXX = mf.e_tot - eng_xc + (1 - ni.hybrid_coeff(mf.xc)) * eng_exactX\n",
    "    # density fitting \n",
    "    int2c2e = mol_df.intor(\"int2c2e\")\n",
    "    int3c2e = df.incore.aux_e2(mol, mol_df)\n",
    "    int2c2e_half = scipy.linalg.cholesky(int2c2e, lower=True)\n",
    "    V_df_mp2 = scipy.linalg.solve_triangular(int2c2e_half, int3c2e.reshape(-1, naux).T, lower=True).reshape(naux, nao, nao).transpose((1, 2, 0))\n",
    "    V_df_ia = (\n",
    "        np.einsum(\"uvP, ui, va -> iaP\", V_df_mp2, Co[0], Cv[0]),\n",
    "        np.einsum(\"uvP, ui, va -> iaP\", V_df_mp2, Co[1], Cv[1]))\n",
    "    V = (V_df_ia[0].reshape(dim_ov[0], naux), V_df_ia[1].reshape(dim_ov[1], naux))\n",
    "    D_ia = (\n",
    "        - eo[0][:, None] + ev[0][None, :],\n",
    "        - eo[1][:, None] + ev[1][None, :])\n",
    "    D = (D_ia[0].flatten(), D_ia[1].flatten())\n",
    "    # dRPA Preparation\n",
    "    Pi_alpha = lambda omega: - 2 * np.einsum(\"dP, d, dQ -> PQ\", V[0], D[0] / (D[0]**2 + omega**2), V[0])\n",
    "    Pi_beta  = lambda omega: - 2 * np.einsum(\"dP, d, dQ -> PQ\", V[1], D[1] / (D[1]**2 + omega**2), V[1])\n",
    "    # dRPA, osRPA1 correlation energy calculation\n",
    "    eng_dRPA, eng_osRPA1, eng_ssRPA = 0, 0, 0\n",
    "    for omega, w_omega in zip(*gen_leggauss_0_inf(40)):\n",
    "        Pi_alpha_matrix, Pi_beta_matrix = Pi_alpha(omega), Pi_beta(omega)\n",
    "        Pi_dRPA_matrix = Pi_alpha_matrix + Pi_beta_matrix\n",
    "        eng_dRPA   += 1 / (2 * np.pi) * w_omega * (np.log(np.linalg.det(np.eye(naux) - Pi_dRPA_matrix )) + Pi_dRPA_matrix.trace() )\n",
    "        eng_ssRPA  += 1 / (2 * np.pi) * w_omega * (np.log(np.linalg.det(np.eye(naux) - Pi_alpha_matrix)) + Pi_alpha_matrix.trace())\n",
    "        eng_ssRPA  += 1 / (2 * np.pi) * w_omega * (np.log(np.linalg.det(np.eye(naux) - Pi_beta_matrix )) + Pi_beta_matrix.trace() )\n",
    "        eng_osRPA1 += 1 / (2 * np.pi) * w_omega * (Pi_alpha_matrix.trace() + Pi_beta_matrix.trace())\n",
    "        W_alpha_matrix, W_beta_matrix = np.zeros((naux, naux)), np.zeros((naux, naux))\n",
    "        for lambd, w_lambd in zip(*gen_leggauss_0_1(10)):\n",
    "            W_alpha_matrix += w_lambd * np.linalg.inv(np.eye(naux) - lambd * Pi_alpha_matrix)\n",
    "            W_beta_matrix  += w_lambd * np.linalg.inv(np.eye(naux) - lambd * Pi_beta_matrix)\n",
    "        eng_osRPA1 += - 1 / (2 * np.pi) * w_omega * ((W_alpha_matrix @ Pi_beta_matrix).trace())\n",
    "        eng_osRPA1 += - 1 / (2 * np.pi) * w_omega * ((W_beta_matrix @ Pi_alpha_matrix).trace())\n",
    "    eng_osRPA  = eng_dRPA  - eng_ssRPA\n",
    "    eng_scsRPA = 9/20 * eng_osRPA1 + 3/4 * eng_dRPA\n",
    "    return (\n",
    "        eng_PBE0,\n",
    "        eng_HXX + eng_osRPA,\n",
    "        eng_HXX + eng_dRPA,\n",
    "        eng_HXX + eng_osRPA1,\n",
    "        eng_HXX + eng_scsRPA,\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 297,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_RscsRPA(mol, dfbasis=\"cc-pVTZ-ri\"):\n",
    "    # molecule preparation\n",
    "    mf = dft.RKS(mol, xc=\"PBE0\").run()\n",
    "    eng_PBE0 = mf.e_tot\n",
    "    mol_df = mol.copy()\n",
    "    mol_df.basis = dfbasis\n",
    "    mol_df.build()\n",
    "    # Basic information preparation\n",
    "    nocc, nmo, nao = mol.nelec[0], mol.nao, mol.nao\n",
    "    naux = mol_df.nao\n",
    "    nvir = nmo - nocc\n",
    "    dim_ov = nocc * nvir\n",
    "    so, sv = slice(0, nocc), slice(nocc, nmo)\n",
    "    eri0_ao = mol.intor(\"int2e\")\n",
    "    e, C = mf.mo_energy, mf.mo_coeff\n",
    "    eo, ev = e[so], e[sv]\n",
    "    Co, Cv = C[:, so], C[:, sv]\n",
    "    # eng_HXX calculation\n",
    "    ni = dft.numint.NumInt()\n",
    "    eng_xc = ni.nr_rks(mol, mf.grids, mf.xc, mf.make_rdm1())[1]\n",
    "    eng_exactX = - 0.25 * (mf.get_k(mf.make_rdm1()) * mf.make_rdm1()).sum()\n",
    "    eng_HXX = mf.e_tot - eng_xc + (1 - ni.hybrid_coeff(mf.xc)) * eng_exactX\n",
    "    # density fitting \n",
    "    int2c2e = mol_df.intor(\"int2c2e\")\n",
    "    int3c2e = df.incore.aux_e2(mol, mol_df)\n",
    "    int2c2e_half = scipy.linalg.cholesky(int2c2e, lower=True)\n",
    "    V_df_mp2 = scipy.linalg.solve_triangular(int2c2e_half, int3c2e.reshape(-1, naux).T, lower=True).reshape(naux, nao, nao).transpose((1, 2, 0))\n",
    "    V_df_ia = np.einsum(\"uvP, ui, va -> iaP\", V_df_mp2, Co, Cv)\n",
    "    V = V_df_ia.reshape(dim_ov, naux)\n",
    "    D = (- eo[:, None] + ev[None, :]).flatten()\n",
    "    # dRPA Preparation\n",
    "    Pi_alpha = lambda omega: - 2 * np.einsum(\"dP, d, dQ -> PQ\", V, D / (D**2 + omega**2), V)\n",
    "    # dRPA, osRPA1 correlation energy calculation\n",
    "    eng_dRPA, eng_ssRPA = 0, 0\n",
    "    for omega, w_omega in zip(*gen_leggauss_0_inf(40)):\n",
    "        Pi_alpha_matrix = Pi_alpha(omega)\n",
    "        Pi_dRPA_matrix = 2 * Pi_alpha_matrix\n",
    "        eng_dRPA   += 1 / (2 * np.pi) * w_omega * (np.log(np.linalg.det(np.eye(naux) - Pi_dRPA_matrix )) + Pi_dRPA_matrix.trace() )\n",
    "        eng_ssRPA  += 1 / (2 * np.pi) * w_omega * (np.log(np.linalg.det(np.eye(naux) - Pi_alpha_matrix)) + Pi_alpha_matrix.trace()) * 2\n",
    "    eng_osRPA1 = eng_ssRPA\n",
    "    eng_osRPA  = eng_dRPA - eng_ssRPA\n",
    "    eng_scsRPA = 9/20 * eng_osRPA1 + 3/4 * eng_dRPA\n",
    "    return (\n",
    "        eng_PBE0,\n",
    "        eng_HXX + eng_osRPA,\n",
    "        eng_HXX + eng_dRPA,\n",
    "        eng_HXX + eng_osRPA1,\n",
    "        eng_HXX + eng_scsRPA,\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下述程序是计算处于解离态的氮原子能量。列表中的数值有 5 项，分别为 PBE0, osRPA, dRPA, osRPA1, scsRPA 的结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 300,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_result_N():\n",
    "    mol = gto.M(atom=\"N 0 0 0\", basis=\"cc-pVTZ\", spin=3, verbose=0)\n",
    "    return get_UscsRPA(mol)\n",
    "\n",
    "result_N = np.array(get_result_N())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下述程序是计算处于解离态的氮气能量。列表中的数值亦有 5 项。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 302,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_result_N2(bond_length):\n",
    "    mol = gto.M(atom=\"N 0 0 0; N 0 0 {:16.8f}\".format(bond_length), basis=\"cc-pVTZ\", verbose=0)\n",
    "    return get_RscsRPA(mol)\n",
    "\n",
    "bond_length_list = np.logspace(np.log10(0.8), np.log10(4.0), 30)\n",
    "results_N2 = np.array([get_result_N2(length) for length in bond_length_list])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后我们就可以对计算所得到的能量作图。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 312,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_list = bond_length_list\n",
    "y_list = (results_N2 - 2 * result_N).T * 627.5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 323,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support. ' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        fig.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option);\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"\" width=\"600\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots(figsize=(6, 4))\n",
    "ax.plot(x_list, y_list[0], c=\"black\", linestyle=\"-.\", label=\"PBE0\")\n",
    "ax.plot(x_list, y_list[1], c=\"C2\", linestyle=\"-.\", label=\"osRPA\")\n",
    "ax.plot(x_list, y_list[2], c=\"C0\", linestyle=\"--\", label=\"dRPA\")\n",
    "ax.plot(x_list, y_list[3], c=\"C1\", linestyle=\"-\", label=\"osRPA1\")\n",
    "ax.plot(x_list, y_list[4], c=\"C6\", linestyle=\"-.\", label=\"scsRPA\")\n",
    "ax.plot(x_list, np.zeros_like(x_list), c=\"black\", linewidth=0.5)\n",
    "ax.set_ylim(-300, 400)\n",
    "ax.legend()\n",
    "ax.set_xlabel(\"Bond Length (Angstrom)\")\n",
    "ax.set_ylabel(\"Relative energy curves (kcal/mol)\")\n",
    "ax.set_title(\"$\\mathsf{N_2}$ dissociation curve without breaking spin symmetry\")\n",
    "fig.tight_layout()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于 CCSD 与 CASSCF 的收敛情况似乎容易存在问题，因此这里仅仅计算了这五条曲线。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[^Zhang-Xu.JPCL.2019.10]: Zhang, I. Y.; Xu, X. Simultaneous Attenuation of Both Self-Interaction Error and Nondynamic Correlation Error in Density Functional Theory: A Spin-Pair Distinctive Adiabatic-Connection Approximation. J. Phys. Chem. Lett. 2019, 10 (10), 2617–2623. doi: [10.1021/acs.jpclett.9b00946](https://doi.org/10.1021/acs.jpclett.9b00946)."
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Raw Cell Format",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
